package com.stox.ui.javafx.core.util;

import java.lang.Thread.UncaughtExceptionHandler;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import javafx.application.Platform;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import com.stox.ui.javafx.core.view.MessageView;

@Component
@Lazy(false)
public class ExceptionHandler implements UncaughtExceptionHandler, BeanPostProcessor{
	private static final Logger logger = LoggerFactory.getLogger(ExceptionHandler.class);
	
	private static MessageSource messageSource;
	
	public static interface ExceptionHandlingRunnable extends Runnable{
		
		public void doRun();
		
		public default void run(){
			try{
				doRun();
			}catch(Throwable throwable){
				logger.error("Error", throwable);
				showErrorOnUi(getMessage(throwable));
				//Thread.getDefaultUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), throwable);
			}
		}
	}
	
	@Autowired
	public void setMessageSource(MessageSource messageSource){
		ExceptionHandler.messageSource = messageSource;
	}
	
	private List<UncaughtExceptionHandler> uncaughtExceptionHandlers = new ArrayList<UncaughtExceptionHandler>();
	
	public ExceptionHandler() {
		Thread.setDefaultUncaughtExceptionHandler(this);
	}
	
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		return bean;
	}
	
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {
		if(bean instanceof UncaughtExceptionHandler && !uncaughtExceptionHandlers.contains(bean)){
			uncaughtExceptionHandlers.add((UncaughtExceptionHandler)bean);
		}
		return bean;
	}
	
	public static void showErrorOnUi(String message){
		Platform.runLater(new ExceptionHandlingRunnable(){
			@Override
			public void doRun() {
				MessageView view = new MessageView();
				view.setTitle(messageSource.getMessage("Error", null, Locale.getDefault()));
				view.setError(message);
				view.open();
			}
		});
	}
	
	public static String getMessage(Throwable e){
		Throwable rootCause = getCause(e);
		String message = rootCause.getMessage();
		if(null == message) message = e.getMessage();
		if(null == message) message = messageSource.getMessage("message.error.default", null, Locale.getDefault());
		return message;
	}
	
	@Override
	public void uncaughtException(Thread t, Throwable e) {
		logger.error(getMessage(e), e);
		for(UncaughtExceptionHandler uncaughtExceptionHandler : uncaughtExceptionHandlers){
			uncaughtExceptionHandler.uncaughtException(t, e);
		}
	}
	
	public static Throwable getCause(Throwable throwable){
		return null == throwable.getCause() ? throwable : getCause(throwable.getCause());
	}

}
